classdef Toptica < handle
    %TOPTICA Summary of this class goes here
    %   Detailed explanation goes here
    
    properties
        name
        device_ip        
        device_hdl
        wlm
        wlmPort
    end
    
    properties(Access=private)
        minPiezoVoltage
        maxPiezoVoltage
        minCurrent
        maxCurrent
        centerFrequency
    end
    
    methods
        function obj = Toptica(name, ip)
            obj.name = name;
            
            switch name
                case {'toptica82', 'Toptica82'}
                    if nargin ==1
                        ip = '192.168.1.131';
                    end
                    obj.device_ip = ip;
                    obj.connect_device();
                    
                    obj.minPiezoVoltage = 10.0; % V
                    obj.maxPiezoVoltage = 100.0; % V
                    obj.minCurrent = 100.0; % mA
                    obj.maxCurrent = 300.0; % mA                    
                    obj.centerFrequency = 384.230e6; % Center of Rb D2 line                  
                case {'toptica87', 'Toptica87'}
                    if nargin ==1
                        ip = '192.168.1.129';
                    end
                    obj.device_ip = ip;
                    obj.connect_device();
                    
                    obj.minPiezoVoltage = 10.0; % V
                    obj.maxPiezoVoltage = 100.0; % V
                    obj.minCurrent = 100.0; % mA
                    obj.maxCurrent = 300.0; % mA                    
                    obj.centerFrequency = 384.230e6; % Center of Rb D2 line
                case {'toptica64', 'Toptica64'}
                    if nargin ==1
                        ip = '192.168.1.130';
                    end
                    obj.device_ip = ip;
                    obj.connect_device();
                    
                    obj.minPiezoVoltage = 10.0; % V
                    obj.maxPiezoVoltage = 100.0; % V
                    obj.minCurrent = 100.0; % mA
                    obj.maxCurrent = 170.0; % mA                    
                    obj.centerFrequency = 377.107e6; % Center of Rb D1 line
            end
        end
        
        function delete(obj)
            obj.disconnect_device();
        end
        
        %% General Operation
        function obj = connect_device(obj)
            L = log4m.getLogger;
            obj.device_hdl = tcpip(obj.device_ip, 1998);
            fopen(obj.device_hdl); pause(0.5);
            if obj.device_hdl.BytesAvailable > 0
                machineStr = fread(obj.device_hdl, obj.device_hdl.BytesAvailable);
            else
                machineStr = '[EMPTY]';
            end
            str = sprintf('%s', machineStr);
            L.info('Toptica::connect_device', str);
        end
        
        function obj = clearOutputBuffer(obj)
            L = log4m.getLogger;
            L.info('Toptica::clearOutputBuffer', sprintf('%d byte(s) available', obj.device_hdl.BytesAvailable));
            if obj.device_hdl.BytesAvailable > 0
                bufferStr = fread(obj.device_hdl, obj.device_hdl.BytesAvailable);
                L.info('Toptica::clearOutputBuffer', sprintf('BufferStr = \n%s', bufferStr));
            end
        end
        
        function obj = disconnect_device(obj)
            L = log4m.getLogger;
            fclose(obj.device_hdl);
            L.info('Toptica::disconnect_device', 'Device disconnected.');
        end
        
        function obj = write(obj, cmd)
            new_line = char(10); %#ok<CHARTEN>
            if strcmp(cmd(end), new_line)
                fwrite(obj.device_hdl, cmd);
            else
                fwrite(obj.device_hdl, [cmd new_line]);
            end            
        end
        
        function res = read(obj, cmd)
            new_line = char(10); %#ok<CHARTEN>
            if strcmp(cmd(end), new_line)
                res = query(obj.device_hdl, cmd);
            else
                res = query(obj.device_hdl, [cmd new_line]);
            end            
        end
        
        function obj = setParam(obj, param, val)
            L = log4m.getLogger;
            cmdStr = ['(param-set! `' param ' ' num2str(val) ')'];
            L.info('Toptica::setParam', sprintf('%s: command sent to device: %s\n', datestr(now,'HH:MM:SS.FFF, mm/dd/yy'), cmdStr));
            obj.write(cmdStr);
        end
        
        function val = getParam(obj, param)
            L = log4m.getLogger;
            cmdStr = ['(param-ref `' param  ')'];
            L.info('Toptica::getParam', sprintf('%s: command sent to device: %s\n', datestr(now,'HH:MM:SS.FFF, mm/dd/yy'), cmdStr));
            val = obj.read(cmdStr);
        end
        
        %% Comm wavelength meter
        function connectWaveLengthMeter(obj, wlm, port)
            obj.wlm = wlm;
            obj.wlmPort = port;
        end
        
        function freq = getFrequency(obj, varargin)
            L = log4m.getLogger;
            p = inputParser;
            p.addParameter('pause', 0.2, @(x) x>=0);
            p.parse(varargin{:});
            
            absFreq = obj.wlm.GetFrequency(obj.wlmPort);
            freq = 1e-3*(absFreq-obj.centerFrequency); % GHz relative to center
            L.info('Toptica::getFrequency', sprintf('Freq = %9.6e', absFreq));
            pause(p.Results.pause);
        end
        
        
        %% PC/CC
        function obj = setPiezoVoltage(obj, volt, varargin)
            p = inputParser;
            voltageCheck = @(val) isnumeric(val) && obj.minPiezoVoltage <= val && val <= obj.maxPiezoVoltage;
            p.addRequired('volt', voltageCheck);
            p.addParameter('pause', 0.1, @(x) x>=0);
            p.parse(volt, varargin{:});            
            obj.setParam('laser1:dl:pc:voltage-set', p.Results.volt);
            pause(p.Results.pause);
        end
        function volt = getActPiezoVotage(obj)
            obj.clearOutputBuffer();
            voltStr = obj.getParam('laser1:dl:pc:voltage-act');
            volt = str2double(voltStr);
        end
        function volt = getSetPiezoVotage(obj)
            obj.clearOutputBuffer();
            voltStr = obj.getParam('laser1:dl:pc:voltage-set');
            volt = str2double(voltStr);
        end
        
        
        function obj = setCurrent(obj, current, varargin)
            p = inputParser;            
            currentCheck = @(val) isnumeric(val) && obj.minCurrent <= val && val <= obj.maxCurrent;
            p.addRequired('current', currentCheck);
            p.addParameter('pause', 0.1, @(x) x>=0);
            p.parse(current, varargin{:});            
            
            obj.setParam('laser1:dl:cc:current-set', p.Results.current);
            pause(p.Results.pause);
        end
        function current = getActCurrent(obj)
            obj.clearOutputBuffer();
            currentStr = obj.getParam('laser1:dl:cc:current-act');
            current = str2double(currentStr);
        end
        function current = getSetCurrent(obj)
            obj.clearOutputBuffer();
            currentStr = obj.getParam('laser1:dl:cc:current-set');
            current = str2double(currentStr);
        end
        
        %% Scan
        function [freqList, cList] = scanCurrent(obj, cList, varargin)
            p = inputParser;            
            p.addRequired('cList', @isnumeric);
            p.addParameter('pause', 0.1, @(x) x>=0);
            p.addParameter('isPlot', true, @islogical);
            p.parse(cList, varargin{:});            
            
            
            obj.setCurrent(cList(1));
            fprintf('Freq = %5.3f', obj.getFrequency());
            
            if p.Results.isPlot
                figure;
            end
            
            freqList = nan(size(cList)); k = 1;
            for c = cList
                obj.setCurrent(c, 'pause', p.Results.pause);
                freqList(k) = obj.getFrequency();
                k = k + 1;
                if p.Results.isPlot
                    plot(cList, freqList, 'bo-'); grid on;
                end
            end
        end
        
        function [freqList, vList] = scanVoltage(obj, vList, varargin)
            p = inputParser;            
            p.addRequired('vList', @isnumeric);
            p.addParameter('pause', 0.1, @(x) x>=0);
            p.addParameter('isPlot', true, @islogical);
            p.parse(vList, varargin{:});            

            obj.setPiezoVoltage(vList(1));
            fprintf('Freq = %5.3f', obj.getFrequency());
            
            if p.Results.isPlot
                figure;
            end
            
            freqList = nan(size(vList)); k = 1;
            for v = vList
                obj.setPiezoVoltage(v, 'pause', p.Results.pause);
                freqList(k) = obj.getFrequency();
                k = k + 1;
                if p.Results.isPlot
                    plot(vList, freqList, 'bo-'); grid on;
                end
            end
        end
        
        function [freqListForward, cListForward, freqListBackward, cListBackward] = scanCurrentBidirection(obj, cList, varargin) 
            [freqListForward, cListForward] = scanCurrent(obj, cList, varargin{:});
            [freqListBackward, cListBackward] = scanCurrent(obj, cList(end:-1:1), varargin{:});
            figure;
            plot(cListForward, freqListForward, 'ro-', cListBackward, freqListBackward, 'bd-'); grid on;
        end
        
        function [freqList, vList, cList] = feedforwardScan(obj, vList, func, varargin)
            p = inputParser;            
            p.addRequired('vList', @isnumeric);
            p.addRequired('func', @(f) isa(f, 'function_handle'));
            p.addParameter('pause', 0.1, @(x) x>=0);
            p.addParameter('isPlot', true, @islogical);
            p.parse(vList, func, varargin{:});
            
            obj.setPiezoVoltage(vList(1));
            fprintf('Freq = %5.3f', obj.getFrequency());
            obj.setCurrent(func(vList(1)));
            fprintf('Freq = %5.3f', obj.getFrequency());
            
            if p.Results.isPlot
                figure;
            end
            
            cList = nan(size(vList));
            freqList = nan(size(vList)); k = 1;
            for v = vList
                cList(k) = func(v);
                obj.setPiezoVoltage(v, 'pause', 0.0);
                obj.setCurrent(cList(k), 'pause', p.Results.pause);                
                freqList(k) = obj.getFrequency();
                k = k + 1;
                if p.Results.isPlot
                    plot(vList, freqList, 'bo-'); grid on;
                end
            end
        end

        %% Feed Forawrd
        function obj = enableFeedforward(obj)
            obj.setParam('laser1:dl:cc:feedforward-enabled', '#t');
        end
        function obj = disableFeedforward(obj)
            obj.setParam('laser1:dl:cc:feedforward-enabled', '#f');
        end
        function tf = getFeedfowardStatus(obj)
            obj.clearOutputBuffer();
            tfStr = obj.getParam('laser1:dl:cc:feedforward-enabled');
            switch tfStr(1:end-1)
                case '#t'
                    tf = true;
                case '#f'
                    tf = false;
            end
        end
        
%         function obj = setScanOffsetVolt(obj, val)
%             if isnumeric(val) && 25 <= val && val < 110
%                 obj.setParam('laser1:scan:offset', val);
%             else
%                 error('Offset votage %f (V): out of range.', val);
%             end
%         end
%         
%         function obj = setScanAmplitudeVolt(obj, val)
%             if isnumeric(val) && 0 < val && val < 50
%                 obj.setParam('laser1:scan:amplitude', val);
%             else
%                 error('Amplitude votage %f (V): out of range.', val);
%             end
%         end
%         
%         function obj = enableScan(obj)
%             obj.setParam('laser1:scan:enabled', '#t');
%         end
%         
%         function obj = disableScan(obj)
%             obj.setParam('laser1:scan:enabled', '#f');
%         end
        
        %% set Current
%         function obj = setCurrent(obj, val)
%             obj.setParam('laser1:dl:cc:current-set', val);
%         end
        
    end
    
    methods
        freqChart = ScanFreqChart(obj, voltList, currentList, varargin)
    end
end

